import os
import json
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, simpledialog
from pathlib import Path
from collections import OrderedDict


class AppConfigManager:
    """应用程序配置管理"""
    CONFIG_FILE = "../a/md_generator_config.json"
    MAX_HISTORY = 10  # 最大历史记录数量

    def __init__(self):
        self.config = {
            "project_paths": OrderedDict(),
            "output_paths": OrderedDict(),
            "profiles": {
                "默认模式": {  # 确保默认模式存在
                    "exclude_dirs": [".git", "node_modules", "venv", "__pycache__", "dist"],
                    "exclude_files": ["package-lock.json", "yarn.lock", ".DS_Store"]
                }
            },
            "last_used": {
                "project": "",
                "output": "",
                "profile": "默认模式"  # 默认使用"默认模式"
            }
        }
        self.load_config()  # 加载本地配置（覆盖默认值）

    def load_config(self):
        """加载配置文件（避免覆盖默认值）"""
        try:
            if os.path.exists(self.CONFIG_FILE):
                with open(self.CONFIG_FILE, "r", encoding="utf-8") as f:
                    loaded = json.load(f)

                    # 仅更新现有配置，不覆盖默认值
                    if "profiles" in loaded:
                        for name, profile in loaded["profiles"].items():
                            if name not in self.config["profiles"]:  # 避免覆盖
                                self.config["profiles"][name] = profile

                    # 更新其他配置（项目路径、输出路径等）
                    self.config["project_paths"] = OrderedDict(loaded.get("project_paths", {}))
                    self.config["output_paths"] = OrderedDict(loaded.get("output_paths", {}))
                    self.config["last_used"] = loaded.get("last_used", self.config["last_used"])
        except Exception as e:
            messagebox.showwarning("警告", f"加载配置文件失败: {str(e)}")
    def save_config(self):
        """保存配置文件"""
        try:
            with open(self.CONFIG_FILE, "w", encoding="utf-8") as f:
                json.dump(self.config, f, indent=2, ensure_ascii=False)  # 确保非ASCII字符正常保存
        except Exception as e:
            messagebox.showerror("错误", f"保存配置文件失败: {str(e)}")
    def add_project_path(self, path):
        """添加项目路径到历史记录"""
        if path and os.path.exists(path):
            self.config["project_paths"].pop(path, None)  # 移除旧位置（如果有）
            self.config["project_paths"][path] = os.path.getmtime(path)  # 使用修改时间排序
            # 保持有序并限制数量
            self.config["project_paths"] = OrderedDict(
                sorted(self.config["project_paths"].items(),
                       key=lambda x: x[1],
                       reverse=True
                       )  # 这里缺少了闭合括号
            )  # 这行应该缩进到if语句内部
            if len(self.config["project_paths"]) > self.MAX_HISTORY:
                self.config["project_paths"].popitem(last=False)
            self.config["last_used"]["project"] = path
            self.save_config()

    def add_output_path(self, path):
        """添加输出路径到历史记录"""
        if path:
            self.config["output_paths"].pop(path, None)
            self.config["output_paths"][path] = os.path.getmtime(Path(path).parent)
            self.config["output_paths"] = OrderedDict(
                sorted(self.config["output_paths"].items(),
                       key=lambda x: x[1],
                       reverse=True)
            )
            if len(self.config["output_paths"]) > self.MAX_HISTORY:
                self.config["output_paths"].popitem(last=False)
            self.config["last_used"]["output"] = path
            self.save_config()

    def get_project_paths(self):
        """获取项目路径列表（按最近使用排序）"""
        return list(self.config["project_paths"].keys())

    def get_output_paths(self):
        """获取输出路径列表（按最近使用排序）"""
        return list(self.config["output_paths"].keys())

    def update_last_used_profile(self, profile_name):
        """更新最后使用的过滤模式"""
        self.config["last_used"]["profile"] = profile_name
        self.save_config()


class MDGeneratorApp:
    def __init__(self, master):
        self.master = master
        master.title("智能文档生成器 v5.0")
        master.geometry("850x650")

        # 初始化配置管理器
        self.config_manager = AppConfigManager()

        # 默认配置
        self.current_profile = tk.StringVar()
        self.exclude_dirs = [".git", "node_modules", "venv", "__pycache__", "dist"]
        self.exclude_files = ["package-lock.json", "yarn.lock", ".DS_Store"]
        self.code_exts = [
            ".py", ".js", ".html", ".css", ".vue",
            ".java", ".c", ".cpp", ".h", ".json",
            ".xml", ".yml", ".yaml", ".md", ".txt"
        ]

        # 创建UI
        self.create_widgets()

        # 加载上次使用的配置
        self.load_last_used_config()
        # 添加窗口关闭事件处理
        master.protocol("WM_DELETE_WINDOW", self.on_close)

    def on_close(self):
        """窗口关闭时保存配置"""
        self.config_manager.save_config()
        self.master.destroy()

    def create_widgets(self):
        """创建所有UI组件"""
        # 主框架
        main_frame = ttk.Frame(self.master, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)

        # 项目目录选择
        dir_frame = ttk.LabelFrame(main_frame, text="项目目录", padding=10)
        dir_frame.pack(fill=tk.X, pady=5)

        # 项目路径选择组合框
        project_select_frame = ttk.Frame(dir_frame)
        project_select_frame.pack(fill=tk.X, pady=2)
        ttk.Label(project_select_frame, text="选择:").pack(side=tk.LEFT)
        self.project_path = tk.StringVar()
        self.project_combobox = ttk.Combobox(
            project_select_frame,
            textvariable=self.project_path,
            values=self.config_manager.get_project_paths(),
            state="readonly"
        )
        self.project_combobox.pack(side=tk.LEFT, padx=5, expand=True, fill=tk.X)
        self.project_combobox.bind("<<ComboboxSelected>>", self.on_project_selected)

        # 项目路径操作按钮
        project_btn_frame = ttk.Frame(dir_frame)
        project_btn_frame.pack(fill=tk.X, pady=2)
        ttk.Button(project_btn_frame, text="浏览...", command=self.select_project_dir).pack(side=tk.LEFT, padx=2)
        ttk.Button(project_btn_frame, text="快速访问", command=self.show_quick_access).pack(side=tk.LEFT, padx=2)
        ttk.Button(project_btn_frame, text="清除历史", command=self.clear_project_history).pack(side=tk.RIGHT, padx=2)

        # 输出文件选择
        output_frame = ttk.LabelFrame(main_frame, text="输出文件", padding=10)
        output_frame.pack(fill=tk.X, pady=5)

        # 输出路径选择组合框
        output_select_frame = ttk.Frame(output_frame)
        output_select_frame.pack(fill=tk.X, pady=2)
        ttk.Label(output_select_frame, text="选择:").pack(side=tk.LEFT)
        self.output_path = tk.StringVar()
        self.output_combobox = ttk.Combobox(
            output_select_frame,
            textvariable=self.output_path,
            values=self.config_manager.get_output_paths(),
            state="readonly"
        )
        self.output_combobox.pack(side=tk.LEFT, padx=5, expand=True, fill=tk.X)
        self.output_combobox.bind("<<ComboboxSelected>>", self.on_output_selected)

        # 输出路径操作按钮
        output_btn_frame = ttk.Frame(output_frame)
        output_btn_frame.pack(fill=tk.X, pady=2)
        ttk.Button(output_btn_frame, text="浏览...", command=self.select_output_file).pack(side=tk.LEFT, padx=2)
        ttk.Button(output_btn_frame, text="清除历史", command=self.clear_output_history).pack(side=tk.RIGHT, padx=2)

        # 过滤模式管理
        profile_frame = ttk.LabelFrame(main_frame, text="过滤模式管理", padding=10)
        profile_frame.pack(fill=tk.X, pady=5)

        # 模式选择下拉菜单
        profile_select_frame = ttk.Frame(profile_frame)
        profile_select_frame.pack(fill=tk.X, pady=2)
        ttk.Label(profile_select_frame, text="选择模式:").pack(side=tk.LEFT)
        self.profile_combobox = ttk.Combobox(
            profile_select_frame,
            textvariable=self.current_profile,
            state="readonly"
        )
        self.profile_combobox.pack(side=tk.LEFT, padx=5, expand=True)
        self.profile_combobox.bind("<<ComboboxSelected>>", self.on_profile_selected)

        # 模式操作按钮
        profile_btn_frame = ttk.Frame(profile_frame)
        profile_btn_frame.pack(fill=tk.X, pady=2)
        ttk.Button(profile_btn_frame, text="保存当前", command=self.save_current_profile).pack(side=tk.LEFT, padx=2)
        ttk.Button(profile_btn_frame, text="删除", command=self.delete_profile).pack(side=tk.LEFT, padx=2)
        ttk.Button(profile_btn_frame, text="新建", command=self.create_new_profile).pack(side=tk.LEFT, padx=2)

        # 排除配置
        config_frame = ttk.LabelFrame(main_frame, text="排除配置", padding=10)
        config_frame.pack(fill=tk.BOTH, expand=True, pady=5)

        # 排除目录配置
        ttk.Label(config_frame, text="排除目录:").grid(row=0, column=0, sticky="w", pady=2)
        self.exclude_dirs_entry = tk.Text(config_frame, width=40, height=3)
        self.exclude_dirs_entry.grid(row=0, column=1, sticky="ew", padx=5, pady=2)

        # 排除文件配置
        ttk.Label(config_frame, text="排除文件:").grid(row=1, column=0, sticky="w", pady=2)
        self.exclude_files_entry = tk.Text(config_frame, width=40, height=3)
        self.exclude_files_entry.grid(row=1, column=1, sticky="ew", padx=5, pady=2)

        # 进度条
        self.progress = ttk.Progressbar(main_frame, orient="horizontal", mode="determinate")
        self.progress.pack(fill=tk.X, pady=10)

        # 生成按钮
        self.generate_btn = ttk.Button(main_frame, text="开始生成", command=self.start_generation)
        self.generate_btn.pack(pady=5)

        # 状态栏
        self.status_label = ttk.Label(main_frame, text="就绪", foreground="gray")
        self.status_label.pack(fill=tk.X)

        # 更新下拉菜单
        self.update_comboboxes()

    def update_comboboxes(self):
        """更新所有下拉菜单"""
        # 更新项目路径下拉菜单
        self.project_combobox["values"] = self.config_manager.get_project_paths()

        # 更新输出路径下拉菜单
        self.output_combobox["values"] = self.config_manager.get_output_paths()

        # 更新过滤模式下拉菜单
        profiles = list(self.config_manager.config["profiles"].keys())
        self.profile_combobox["values"] = profiles
        if profiles and not self.current_profile.get():
            self.current_profile.set(profiles[0])

    def load_last_used_config(self):
        """加载上次使用的配置"""
        last_project = self.config_manager.config["last_used"]["project"]
        if last_project and os.path.exists(last_project):
            self.project_path.set(last_project)

        last_output = self.config_manager.config["last_used"]["output"]
        if last_output:
            self.output_path.set(last_output)

        # 确保 profile 存在，否则使用默认模式
        last_profile = self.config_manager.config["last_used"]["profile"]
        if last_profile in self.config_manager.config["profiles"]:
            self.current_profile.set(last_profile)
        else:
            self.current_profile.set("默认模式")  # 回退到默认模式

        self.on_profile_selected()  # 强制加载当前选择的模式
        self.update_config_entries()  # 更新UI显示
    def load_default_profile(self):
        """加载默认过滤模式"""
        if "默认模式" in self.config_manager.config["profiles"]:
            profile = self.config_manager.config["profiles"]["默认模式"]
            self.exclude_dirs = profile["exclude_dirs"]
            self.exclude_files = profile["exclude_files"]
        self.update_config_entries()

    def update_config_entries(self):
        """更新配置输入框内容"""
        self.exclude_dirs_entry.delete("1.0", tk.END)
        self.exclude_dirs_entry.insert("1.0", "\n".join(self.exclude_dirs))

        self.exclude_files_entry.delete("1.0", tk.END)
        self.exclude_files_entry.insert("1.0", "\n".join(self.exclude_files))

    def on_project_selected(self, event=None):
        """当选择项目路径时触发"""
        selected_path = self.project_path.get()
        if selected_path and os.path.exists(selected_path):
            self.config_manager.add_project_path(selected_path)

    def on_output_selected(self, event=None):
        """当选择输出路径时触发"""
        selected_path = self.output_path.get()
        if selected_path:
            self.config_manager.add_output_path(selected_path)

    def on_profile_selected(self, event=None):
        """当选择过滤模式时触发"""
        profile_name = self.current_profile.get()
        if profile_name in self.config_manager.config["profiles"]:
            profile = self.config_manager.config["profiles"][profile_name]
            self.exclude_dirs = profile["exclude_dirs"]
            self.exclude_files = profile["exclude_files"]
            self.update_config_entries()
            self.config_manager.update_last_used_profile(profile_name)  # 更新最后使用的模式
    def save_current_profile(self):
        """保存当前配置为过滤模式"""
        profile_name = self.current_profile.get()
        if not profile_name:
            messagebox.showwarning("警告", "请输入模式名称")
            return

        exclude_dirs = self.exclude_dirs_entry.get("1.0", tk.END).strip().split("\n")
        exclude_files = self.exclude_files_entry.get("1.0", tk.END).strip().split("\n")

        # 过滤空值
        exclude_dirs = [d.strip() for d in exclude_dirs if d.strip()]
        exclude_files = [f.strip() for f in exclude_files if f.strip()]

        self.config_manager.config["profiles"][profile_name] = {
            "exclude_dirs": exclude_dirs,
            "exclude_files": exclude_files
        }
        self.config_manager.save_config()
        self.update_comboboxes()
        messagebox.showinfo("成功", f"已保存过滤模式: {profile_name}")

    def delete_profile(self):
        """删除当前选择的过滤模式"""
        profile_name = self.current_profile.get()
        if profile_name and messagebox.askyesno("确认", f"确定要删除模式 '{profile_name}' 吗?"):
            if profile_name in self.config_manager.config["profiles"]:
                del self.config_manager.config["profiles"][profile_name]
                self.config_manager.save_config()
                self.update_comboboxes()
                self.load_default_profile()
                messagebox.showinfo("成功", f"已删除模式: {profile_name}")
            else:
                messagebox.showerror("错误", "未找到指定模式")

    def create_new_profile(self):
        """创建新过滤模式"""
        new_name = simpledialog.askstring("新建模式", "请输入新模式名称:")
        if new_name:
            if new_name in self.config_manager.config["profiles"]:
                messagebox.showwarning("警告", "该名称已存在!")
                return

            self.current_profile.set(new_name)
            self.save_current_profile()

    def select_project_dir(self):
        """选择项目目录"""
        initial_dir = self.project_path.get() or os.path.expanduser("~")
        path = filedialog.askdirectory(
            title="选择项目目录",
            initialdir=initial_dir
        )
        if path:
            self.project_path.set(path)
            self.config_manager.add_project_path(path)
            self.update_comboboxes()

    def select_output_file(self):
        """选择输出文件"""
        initial_dir = os.path.dirname(self.output_path.get()) if self.output_path.get() else os.path.expanduser("~")
        path = filedialog.asksaveasfilename(
            title="保存文档",
            defaultextension=".md",
            filetypes=[("Markdown 文件", "*.md"), ("所有文件", "*.*")],
            initialdir=initial_dir
        )
        if path:
            self.output_path.set(path)
            self.config_manager.add_output_path(path)
            self.update_comboboxes()

    def show_quick_access(self):
        """显示快速访问菜单"""
        menu = tk.Menu(self.master, tearoff=0)

        # 添加常用目录
        quick_paths = {
            "桌面": os.path.join(os.path.expanduser("~"), "Desktop"),
            "文档": os.path.join(os.path.expanduser("~"), "Documents"),
            "下载": os.path.join(os.path.expanduser("~"), "Downloads"),
            "项目目录": os.path.join(os.path.expanduser("~"), "Projects")
        }

        for name, path in quick_paths.items():
            if os.path.exists(path):
                menu.add_command(
                    label=f"{name}: {path}",
                    command=lambda p=path: self.set_project_path(p)
                )

        # 添加历史记录
        menu.add_separator()
        history = self.config_manager.get_project_paths()
        if history:
            for path in history:
                menu.add_command(
                    label=f"历史: {path}",
                    command=lambda p=path: self.set_project_path(p)
                )
        else:
            menu.add_command(label="无历史记录", state="disabled")

        # 显示菜单
        try:
            menu.tk_popup(*self.master.winfo_pointerxy())
        finally:
            menu.grab_release()

    def set_project_path(self, path):
        """设置项目路径并更新历史"""
        if os.path.exists(path):
            self.project_path.set(path)
            self.config_manager.add_project_path(path)
            self.update_comboboxes()

    def clear_project_history(self):
        """清除项目路径历史记录"""
        if messagebox.askyesno("确认", "确定要清除所有项目路径历史记录吗?"):
            self.config_manager.config["project_paths"] = OrderedDict()
            self.config_manager.save_config()
            self.update_comboboxes()

    def clear_output_history(self):
        """清除输出路径历史记录"""
        if messagebox.askyesno("确认", "确定要清除所有输出路径历史记录吗?"):
            self.config_manager.config["output_paths"] = OrderedDict()
            self.config_manager.save_config()
            self.update_comboboxes()

    def validate_inputs(self):
        """验证输入是否有效"""
        if not self.project_path.get():
            self.update_status("请选择项目目录", "red")
            return False
        if not self.output_path.get():
            self.update_status("请选择保存路径", "red")
            return False

        # 更新当前配置
        self.exclude_dirs = [
            d.strip() for d in
            self.exclude_dirs_entry.get("1.0", tk.END).split("\n")
            if d.strip()
        ]
        self.exclude_files = [
            f.strip() for f in
            self.exclude_files_entry.get("1.0", tk.END).split("\n")
            if f.strip()
        ]

        return True

    def update_status(self, text, color="black"):
        """更新状态栏"""
        self.status_label.config(text=text, foreground=color)
        self.master.update()

    def set_ui_state(self, enabled):
        """设置UI状态"""
        state = "normal" if enabled else "disabled"
        for widget in [
            self.project_combobox,
            self.output_combobox,
            self.profile_combobox,
            self.exclude_dirs_entry,
            self.exclude_files_entry,
            self.generate_btn
        ]:
            widget.config(state=state)

        if enabled:
            self.progress["value"] = 0

    def show_success(self):
        """显示成功信息"""
        messagebox.showinfo("成功", f"文档已生成: {self.output_path.get()}")
        self.update_status("生成成功", "green")
        self.progress["value"] = 100

    def show_error(self, message):
        """显示错误信息"""
        messagebox.showerror("错误", message)
        self.update_status(f"错误: {message}", "red")
        self.progress["value"] = 0

    def start_generation(self):
        """开始生成文档"""
        if not self.validate_inputs():
            return

        self.set_ui_state(False)
        try:
            self.generate_full_md_structure(
                root_dir=self.project_path.get(),
                output_file=self.output_path.get()
            )
            self.show_success()
        except Exception as e:
            self.show_error(str(e))
        finally:
            self.set_ui_state(True)

    def generate_full_md_structure(self, root_dir, output_file):
        """生成完整项目文档"""
        md_content = ["# 项目结构文档\n\n"]
        total_files = 0
        processed_files = 0

        # 计算总文件数
        for root, dirs, files in os.walk(root_dir):
            dirs[:] = [d for d in dirs if d not in self.exclude_dirs]
            files[:] = [f for f in files if f not in self.exclude_files]
            total_files += len(files)

        if total_files == 0:
            raise ValueError("没有找到可处理的文件")

        # 生成文档内容
        for root, dirs, files in os.walk(root_dir):
            dirs[:] = [d for d in dirs if d not in self.exclude_dirs]

            relative_path = Path(root).relative_to(root_dir)
            depth = len(relative_path.parts)

            if root != root_dir:
                heading_level = min(depth + 1, 6)
                md_content.append(f"{'#' * heading_level} {relative_path}\n\n")

            for file in files:
                if file in self.exclude_files:
                    continue

                file_path = Path(root) / file
                ext = file_path.suffix.lower()

                # 更新进度
                processed_files += 1
                progress = (processed_files / total_files) * 100
                self.progress["value"] = progress
                self.update_status(f"正在处理: {file} ({processed_files}/{total_files})")

                md_content.append(f"### `{file}`\n")

                try:
                    with open(file_path, "r", encoding="utf-8") as f:
                        content = f.read()

                    code_lang = ext[1:] if ext else "text"
                    if any(ext == ext_check for ext_check in self.code_exts):
                        md_content.append(f"```{code_lang}\n{content}\n```\n\n")
                    else:
                        md_content.append(f"```text\n{content}\n```\n\n")

                except UnicodeDecodeError:
                    file_size = os.path.getsize(file_path)
                    md_content.append(f"> 二进制文件（{file_size}字节）无法直接显示\n\n")
                except Exception as e:
                    md_content.append(f"> 文件读取错误: {str(e)}\n\n")

                if processed_files % 10 == 0:
                    self.master.update()

        # 写入文件
        try:
            with open(output_file, "w", encoding="utf-8") as f:
                f.write("".join(md_content))
        except Exception as e:
            raise Exception(f"写入输出文件失败: {str(e)}")


if __name__ == "__main__":
    root = tk.Tk()
    app = MDGeneratorApp(root)
    root.mainloop()